package biz

import (
	"context"
	"errors"
	"fmt"
	"github.com/go-kratos/kratos/v2/log"
	"github.com/gogf/gf/util/gconv"
	"github.com/gookit/goutil/dump"
	amount1_v1 "gorm_transaction/api/Amount1Server/v1"
	"time"
)

/*
CREATE TABLE `cell` (
  `id` int unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
  `parent_id` int unsigned NOT NULL,
  `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `idx_parent_id` (`parent_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
*/

const (
	CellTableName = "cell"
)

type Cell struct {
	Id        uint32     `gorm:"column:id;primary_key" json:"id"`
	Name      string     `gorm:"column:name" json:"name"`
	ParentId  uint32     `gorm:"column:parent_id" json:"parentId"`
	CreatedAt *time.Time `gorm:"column:created_at;default:null" json:"createdAt"`
	UpdatedAt *time.Time `gorm:"column:updated_at;default:null" json:"updatedAt"`
}

func (c *Cell) TableName() string {
	return CellTableName
}

type CellBizInterface interface {
	// 基础查询方法 Notice 只查数据库 不做缓存了～实际业务中如果访问量大的话会做缓存～
	CellFindOneById(ctx context.Context, id uint32) (*Cell, error)
	CellFindManyByParentId(ctx context.Context, parentId uint32) ([]*Cell, error)

	// 递归获取指定id的cell的所有父级数据(有顺序、以当前cell开始)
	RecursionGetParents(ctx context.Context, id uint32) ([]*Cell, error)
}

type CellBizStruct struct {
	cellRepo CellBizInterface
	logger   log.Logger
	// 事务
	TM Transaction
}

func NewCellBiz(cellRepo CellBizInterface, logger log.Logger, tm Transaction) *CellBizStruct {
	return &CellBizStruct{
		cellRepo: cellRepo,
		logger:   logger,
		TM:       tm,
	}
}

// 返回 当前cell以及它所有的子cell
func (c *CellBizStruct) RecursionGetCellChildren(ctx context.Context, req *amount1_v1.RecursionGetCellDataRep) (*amount1_v1.RecursionGetCellChildrenDataLayerReply, error) {

	if req.GetCellId() == 0 {
		return nil, errors.New("传来的cell的id不能为0!")
	}

	reply := &amount1_v1.RecursionGetCellChildrenDataLayerReply{}

	// 先查一下当前的cell
	currCell, errCurrCell := c.cellRepo.CellFindOneById(ctx, req.GetCellId())
	if errCurrCell != nil {
		return nil, errCurrCell
	}
	fmt.Println("currCell: >>>>> ", currCell)

	// 构建protobuf消息
	protoCellNode := &amount1_v1.CellChildrenDataLayer{
		Id:        currCell.Id,
		ParentId:  currCell.ParentId,
		Name:      currCell.Name,
		CreatedAt: gconv.String(currCell.CreatedAt),
		UpdatedAt: gconv.String(currCell.UpdatedAt),
	}

	// 递归获取数据
	c.recursionFindAllChildren(ctx, currCell, protoCellNode)

	fmt.Println("protoCellNode: >>>> ", gconv.String(protoCellNode))

	reply.CellReply = protoCellNode

	return reply, nil
}

// 返回 当前cell以及它所有的父级cell
func (c *CellBizStruct) RecursionGetCellParents(ctx context.Context, req *amount1_v1.RecursionGetCellDataRep) (*amount1_v1.RecursionGetCellParentDataLayerReply, error) {

	if req.GetCellId() == 0 {
		return nil, errors.New("传来的cell的id不能为0!")
	}
	reply := &amount1_v1.RecursionGetCellParentDataLayerReply{}

	res, errRes := c.cellRepo.RecursionGetParents(ctx, req.GetCellId())
	if errRes != nil {
		return nil, errRes
	}
	fmt.Println("res>>>> ")
	dump.P(gconv.String(res))

	// 结构体切片转换
	errStructs := gconv.Structs(res, &reply.CellList)
	if errStructs != nil {
		return nil, errStructs
	}

	return reply, nil
}

func (c *CellBizStruct) recursionFindAllChildren(ctx context.Context, cell *Cell, protoCellNode *amount1_v1.CellChildrenDataLayer) {

	children, errChildren := c.cellRepo.CellFindManyByParentId(ctx, cell.Id)
	if errChildren != nil {
		fmt.Println(">>> errChildren: ", children)
	}

	fmt.Printf("%v 的 children:>>>> %v, %v\n", cell.Id, len(children), children)

	// Notice 递归结束的条件: 没有children了～
	for _, child := range children {
		protoChild := &amount1_v1.CellChildrenDataLayer{
			Id:        child.Id,
			ParentId:  child.ParentId,
			Name:      child.Name,
			CreatedAt: gconv.String(child.CreatedAt),
			UpdatedAt: gconv.String(child.UpdatedAt),
		}
		protoCellNode.Children = append(protoCellNode.Children, protoChild)
		//fmt.Println("protoCellNode<<<<<<<<< ", len(protoCellNode.Children), gconv.String(protoCellNode))
		// 递归
		c.recursionFindAllChildren(ctx, child, protoChild)
	}
}
